﻿// -----------------------------------------------------------------------
// <copyright file="ProjectedValue.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace ClearLines.Stocks.Domain
{
   using System;
   using System.Collections.Concurrent;
   using System.Collections.Generic;
   using System.Linq;
   using System.Text;
   using System.Threading.Tasks;

   /// <summary>
   /// TODO: Update summary.
   /// </summary>
   public class ProjectedValue
   {
      private static Random seedsGenerator = new Random();

      private static Random Rng()
      {
         
         lock (seedsGenerator)
         {
            var seed = seedsGenerator.Next();
            return new Random(seed);
         }
      }

      public static List<double> Run(StockHistory history, int horizon, int runs)
      {
         var closeSeries = history.History.Select(it => (double)it.Close).ToList();
         var returnsSeries = new List<double>();
         for (var i = 0; i < closeSeries.Count() - 1; i++)
         {
            var today = closeSeries[i];
            var tomorrow = closeSeries[i + 1];
            var rate = (tomorrow - today) / today;
            returnsSeries.Add(rate);
         }

         var initialValue = 100d;
         var results = new ConcurrentBag<double>();

         Parallel.For(0, runs,
             (run) =>
             {
                var rng = Rng();
                var endValue = Simulate(initialValue, horizon, returnsSeries, rng);
                results.Add(endValue);
             });

         return results.ToList();
      }

      public static double Simulate(double initialValue, int horizon, List<double> sampleDailyReturn, Random random)
      {
         var sampleSize = sampleDailyReturn.Count();
         var simulatedReturns = new List<double>();
         for (var day = 0; day < horizon; day++)
         {
            var pick = random.Next(sampleSize);
            simulatedReturns.Add(sampleDailyReturn[pick]);
         }

         return simulatedReturns.Aggregate(initialValue, (value, returnRate) => value * (1d + returnRate));
      }
   }
}
